home *** CD-ROM | disk | FTP | other *** search
/ The 640 MEG Shareware Studio 2 / The 640 Meg Shareware Studio CD-ROM Volume II (Data Express)(1993).ISO / pascal / pcxkt3.zip / PCX.PAS < prev    next >
Pascal/Delphi Source File  |  1992-03-26  |  24KB  |  628 lines

  1. unit PCX;
  2.  
  3. (* version 3.1
  4.                              by Peter Donnelly
  5.                               1301 Ryan Street
  6.                                 Victoria BC
  7.                                Canada V8T 4Y8
  8.  
  9.    ╒══════════════════════════════════════════════════════════════════════╕
  10.    │    May be copied freely. If you make practical use of this unit,     │
  11.    │         a contribution of $10 or more would be appreciated.          │     
  12.    ╘══════════════════════════════════════════════════════════════════════╛
  13.  
  14.    This is a unit to read .PCX files and put them in displayable form. The
  15.    actual work of decoding the file and moving the data into memory is done
  16.    in assembler. Version 6 of Turbo Pascal is required for compilation.
  17.  
  18.    The following display modes are supported:
  19.  
  20.           Mode      TP GraphMode     Resolution    Colors
  21.           ~~~~      ~~~~~~~~~~~~     ~~~~~~~~~~    ~~~~~~
  22.           $04       CGAC0 to C3      320 x 200         4
  23.           $06       CGAHi            640 x 200         2
  24.           $0D        ---             320 x 200        16
  25.           $0E       EGALo/VGALo      640 x 200        16
  26.           $10       EGAHi/VGAMed     640 x 350        16
  27.           $12       VGAHi            640 x 480        16
  28.           $13        ---             320 x 200       256
  29.  
  30.    Mode $13 is supported only for files containing palette information,
  31.    i.e. not those produced by versions of Paintbrush earlier than 3.0.
  32.  
  33.    The unit has been optimized for speed rather than flexibility or
  34.    compactness. In particular, the routine for displaying 16-color files
  35.    (which require the most computation) has been improved and now runs    
  36.    about 50 percent faster than that in version 2.
  37.  
  38.    It is assumed that the image is the width of and no taller than the
  39.    screen, and that you will set the correct display mode. No checking
  40.    is done to see that the .PCX file is compatible with the mode you've set.
  41.    You do, however, have to pass in the Turbo GraphDriver as a parameter
  42.    for all but 256-color files, so that the data will be interpreted
  43.    correctly. (For mode $0D, pass in 'EGA' or 'VGA', and see the comment on
  44.    palettes, below.)
  45.  
  46.    For the CGA formats, the data is put into two buffers on the heap, from
  47.    where it can be moved into the two display memory banks. See SHOWCGA for
  48.    an example. You can of course alter the unit to move the data directly
  49.    into display memory, but there is no great saving in time.
  50.  
  51.    For EGA and VGA formats, the data is written to page 0 of the video
  52.    buffer. This can easily be changed by setting "page_addr" to a different
  53.    value. Three different techniques of hiding the image while it is being
  54.    written are demonstrated in SHOWEGA, SHOWVGA, and SHOW256. If for any
  55.    reason you don't want to do this, you will want to rewrite the palette-
  56.    interpretation routines as separate procedures so you can set the
  57.    palette before decoding the image data.
  58.  
  59.    References:
  60.    ~~~~~~~~~~
  61.    Richard F. Ferraro, "Programmer's Guide to the EGA and VGA Cards"
  62.    (Addison-Wesley, 1988).
  63.  
  64.    Richard Wilton, "Programmer's Guide to PC and PS/2 Video Systems"
  65.    (Microsoft, 1987).
  66.  
  67.    "Technical Reference Manual [for Paintbrush]" (Zsoft, 1988). The
  68.    information in this booklet is also found in a file distributed with
  69.    at least some versions of Microsoft/PC Paintbrush.
  70.  
  71.    Software:
  72.    ~~~~~~~~
  73.    Besides the various incarnations of Paintbrush (ZSoft and Microsoft),
  74.    the excellent Deluxe Paint II Enhanced (Electronic Arts) can also create
  75.    files in .PCX format. Other graphics programs have conversion utilities.
  76. *)
  77.  
  78. { ======================================================================= }
  79.  
  80. INTERFACE
  81.  
  82. uses DOS, GRAPH;
  83.  
  84. type    RGBrec = record
  85.                    redval, greenval, blueval: byte;
  86.                  end;
  87.  
  88. var     pcxfilename: pathstr;
  89.         file_error: boolean;
  90.         pal: palettetype;
  91.         RGBpal: array[0..15] of RGBrec;
  92.         RGB256: array[0..255] of RGBrec;
  93.         page_addr: word;
  94.         bytes_per_line: word;
  95.         buff0, buff1: pointer;
  96.  
  97.         { CGA display memory banks: }
  98.         screenbuff0: array[0..7999] of byte absolute $b800:$0000;
  99.         screenbuff1: array[0..7999] of byte absolute $b800:$2000;
  100.  
  101. const   page0 = $A000;           { EGA/VGA display segment }
  102.  
  103. procedure SETMODE(mode: byte);
  104. procedure SETREGISTERS(var palrec);
  105. procedure READ_PCX_FILE(gdriver: integer; pfilename: pathstr);
  106. procedure READ_PCX256(pfilename: pathstr);
  107.  
  108. {========================================================================}
  109.  
  110. IMPLEMENTATION
  111.  
  112. var     scratch, abuff0, abuff1: pointer;
  113.         is_CGA, is_VGA: boolean;
  114.         repeatcount: byte;
  115.         datalength: word;
  116.         columncount, plane, video_index: word;
  117.         regs: registers;
  118.  
  119. const   buffsize = 65521;   { Largest possible }
  120.  
  121. { -------------------------- BIOS calls --------------------------------- }
  122.  
  123. { For modes not supported by the BGI, use SetMode to initialize the
  124.   graphics. Since SetRGBPalette won't work if Turbo hasn't done the 
  125.   graphics initialization itself, use SetRegisters to change the colors 
  126.   in mode $13. }
  127.  
  128. procedure SETMODE(mode: byte);
  129.  
  130. begin
  131. regs.ah:= 0;                 { BIOS set mode function }
  132. regs.al:= mode;              { Display mode }
  133. intr($10, regs);             { Call BIOS }
  134. end;
  135.  
  136. procedure SETREGISTERS(var palrec);
  137.  
  138. { Palrec is any string of 768 bytes containing the RGB data. }
  139.  
  140. begin
  141. regs.ah:= $10;               { BIOS color register function }
  142. regs.al:= $12;               { Subfunction }
  143. regs.es:= seg(palrec);       { Address of palette info. }
  144. regs.dx:= ofs(palrec);
  145. regs.bx:= 0;                 { First register to change }
  146. regs.cx:= $100;              { Number of registers to change }
  147. intr($10, regs);             { Call BIOS }
  148. end;
  149.  
  150. { ====================== EGA/VGA 16-color files ========================= }
  151.  
  152. procedure DECODE_16; assembler;
  153.  
  154. asm
  155.  
  156. (* Registers used:
  157.  
  158.    AL   data byte to be written to video
  159.    AH   data bytes per scan line
  160.    BX   end of input buffer
  161.    CL   number of times data byte is to be written
  162.    DL   current column in scan line
  163.    ES   output segment
  164.    DI   index into output buffer
  165.    DS   segment of input buffer
  166.    SI   index into input buffer
  167.    BP   current color plane
  168. *)
  169.  
  170. push    bp
  171.  
  172. { ----------------- Assembler procedure for 16-color files -------------- }
  173.  
  174. { The first section is initialization done on each run through the 
  175.   input buffer. }
  176.  
  177. @startproc:
  178. mov     bp, plane           { plane in BP }
  179. mov     es, page_addr       { video display segment }
  180. mov     di, video_index     { index into video segment }
  181. mov     ah, byte ptr bytes_per_line  { line length in AH }
  182. mov     dx, columncount     { column counter }
  183. mov     bx, datalength      { no. of bytes to read }
  184. xor     cx, cx              { clean up CX for loop counter }
  185. mov     cl, repeatcount     { count in CX }
  186. push    ds                  { save DS }
  187. lds     si, scratch         { input buffer pointer in DS:SI }
  188.  { We have to adjust datalength for comparison with SI. TP 6.0 pointers are
  189.    normalized, but the offset can still be 0 or 8. }
  190. add     bx, si
  191. cld                         { clear DF for stosb }
  192. cmp     cl, 0               { was last byte a count? }
  193. jne     @multi_data         { yes, so next is data }
  194. jmp     @getbyte            { no, so find out what next is }
  195.  
  196. { -------------- Procedure to write EGA/VGA image to video -------------- }
  197.  
  198. { The data in the .PCX file is organized by color plane, by line; that is,
  199.   all the data for plane 0 for line 1, then for plane 1, line 1, etc.
  200.   Writing the data to display memory is just a matter of masking out the
  201.   other planes while one plane is being written to. This is done with the
  202.   map mask register in the sequencer. All the other weird and wonderful
  203.   registers in the EGA/VGA do just fine with their default settings, thank
  204.   goodness. }
  205.  
  206. @writebyte:
  207. stosb                       { AL into ES:DI, inc DI }
  208. inc     dl                  { increment column }
  209. cmp     dl, ah              { reached end of scanline? }
  210. je      @doneline           { yes }
  211. loop    @writebyte          { no, do another }
  212. jmp     @getbyte            {   or get more data }
  213. @doneline:
  214. shl     bp, 1               { shift to next plane }
  215. cmp     bp, 8               { done 4 planes? }
  216. jle     @setindex           { no }
  217. mov     bp, 1               { yes, reset plane to 1 but don't reset index }
  218. jmp     @setplane
  219. @setindex:
  220. sub     di, dx              { reset to start of line }
  221. @setplane:
  222. push    ax                  { save AX }
  223. cli                         { no interrupts }
  224. mov     ax, bp              { plane is 1, 2, 4, or 8 }
  225. mov     dx, 3C5h            { sequencer data register }
  226. out     dx, al              { mask out 3 planes }
  227. sti                         { enable interrupts }
  228. pop     ax                  { restore AX }
  229. xor     dx, dx              { reset column count }
  230. loop    @writebyte          { do it again, or fetch more data }
  231.  
  232. { -------------------- Loop through input buffer ------------------------ }
  233.  
  234. { Here's how the data compression system works. Each byte is either image
  235.   data or a count byte that tells how often the next byte is to be
  236.   repeated. The byte is image data if it follows a count byte, or if
  237.   either of the top 2 bits is clear. Otherwise it is a count byte, with
  238.   the count derived from the lower 6 bits. }
  239.  
  240. @getbyte:                   { last byte was not a count }
  241. cmp     si, bx              { end of input buffer? }
  242. je      @exit               { yes, quit }
  243. lodsb                       { get a byte from DS:SI into AL, increment SI }
  244. cmp     al, 192             { test high bits }
  245. jb      @one_data           { not set, it's data to be written once }
  246.  { It's a count byte: }
  247. xor     al, 192             { get count from 6 low bits }
  248. mov     cl, al              { store repeat count }
  249. cmp     si, bx              { end of input buffer? }
  250. je      @exit               { yes, quit }
  251. @multi_data:
  252. lodsb                       { get data byte }
  253. jmp     @writebyte          { write it CL times }
  254. @one_data:
  255. mov     cl, 1               { write byte once }
  256. jmp     @writebyte
  257.  
  258. { ---------------------- Finished with buffer --------------------------- }
  259.  
  260. @exit:
  261. pop     ds                  { restore Turbo's data segment }
  262. mov     plane, bp           { save status for next run thru buffer }
  263. mov     repeatcount, cl
  264. mov     columncount, dx
  265. mov     video_index, di
  266. pop     bp
  267. end;  { asm }
  268.  
  269. { ===================== CGA 2- and 4-color files ======================== }
  270.  
  271. procedure DECODE_CGA; assembler;
  272.  
  273. asm
  274.  
  275. (* Registers used:
  276.  
  277.    AL   data byte to be written to video
  278.    AH   data bytes per scan line
  279.    BX   end of input buffer
  280.    CL   number of times data byte is to be written
  281.    DL   pointer to current column in screen row
  282.    ES   output segment; temporarily used for input buffer segment
  283.    DI   index into output buffer
  284.    SI   index into input buffer
  285.    BP   current video bank
  286. *)
  287.  
  288. push    bp
  289. jmp     @startproc
  290.  
  291. { ------------- Procedure to store CGA image in buffers ----------------- }
  292.  
  293. @storebyte:
  294. stosb                       { AL into ES:DI, increment DI }
  295. inc     dx                  { increment column count }
  296. cmp     dl, ah              { reached end of line? }
  297. je      @row_ends           { yes }
  298. loop    @storebyte          { not end of row, do another byte }
  299. ret
  300. @row_ends:
  301. xor     bp, 1               { switch banks }
  302. cmp     bp, 1               { is bank 1? }
  303. je      @bank1              { yes }
  304. mov     word ptr abuff1, di { no, save index into bank 1 }
  305. les     di, abuff0          { bank 0 pointer into ES:DI }
  306. xor     dx, dx              { reset column counter }
  307. loop    @storebyte
  308. ret
  309. @bank1:
  310. mov     word ptr abuff0, di { save index into bank 0 }
  311. les     di, abuff1          { bank 1 pointer into ES:DI }
  312. xor     dx, dx              { reset column counter }
  313. loop    @storebyte
  314. ret
  315.  
  316. { ---------------- Main assembler procedure for CGA --------------------- }
  317.  
  318. { It's assumed that CGA files will require only one pass through the
  319.   input buffer. }
  320.  
  321. @startproc:
  322. mov     bp, 0                        { bank in BP }
  323. mov     es, word ptr abuff0[2]       { segment of bank 0 buffer }
  324. mov     di, word ptr abuff0          { offset of buffer }
  325. mov     ah, byte ptr bytes_per_line  { line length in AH }
  326. mov     bx, datalength               { no. of bytes to read }
  327. xor     cx, cx                       { clean up CX for loop counter }
  328. xor     dx, dx                       { initialize column counter }
  329. mov     si, dx                       { initialize input index }
  330. cld                                  { clear DF for stosb }
  331.  
  332. { -------------------- Loop through input buffer ------------------------ }
  333.  
  334. @getbyte:
  335. cmp     si, bx              { end of input buffer? }
  336. je      @exit               { yes, quit }
  337. push    es                  { save output pointer }
  338. push    di
  339. les     di, scratch         { get input pointer in ES:DI }
  340. add     di, si              { add current offset }
  341. mov     al, [es:di]         { get a byte }
  342. inc     si                  { advance input index }
  343. pop     di                  { restore output pointer }
  344. pop     es
  345. cmp     cl, 0               { was previous byte a count? }
  346. jg      @multi_data         { yes, this is data }
  347. cmp     al, 192             { no, test high bits }
  348. jb      @one_data           { not set, not a count }
  349.  { It's a count byte: }
  350. xor     al, 192             { get count from 6 low bits }
  351. mov     cl, al              { store repeat count }
  352. jmp     @getbyte            { go get data byte }
  353. @one_data:
  354. mov     cl, 1               { write byte once }
  355. call    @storebyte
  356. jmp     @getbyte
  357. @multi_data:
  358. call    @storebyte          { CL already set }
  359. jmp     @getbyte
  360.  
  361. { ---------------------- Finished with buffer --------------------------- }
  362.  
  363. @exit:
  364. pop     bp
  365. end;  { asm }
  366.  
  367. { ============= Main procedure for CGA and 16-color files =============== }
  368.  
  369. procedure READ_PCX_FILE(gdriver: integer; pfilename: pathstr);
  370.  
  371. type    ptrrec = record
  372.                    segm, offs: word;
  373.                  end;
  374.  
  375. var     entry, gun, pcxcode, mask, colorID: byte;
  376.         palbuf: array[0..66] of byte;
  377.         pcxfile: file;
  378.  
  379. begin   { READ_PCX_FILE }
  380. is_CGA:= (gdriver = CGA);   { 2 or 4 colors }
  381. is_VGA:= (gdriver = VGA);   { 16 of 256K possible colors }
  382.                             { Otherwise EGA - 16 of 64 possible colors }
  383. assign(pcxfile, pfilename);
  384. {$I-} reset(pcxfile, 1);  {$I+}
  385. file_error:= (IOresult <> 0);
  386. if file_error then exit;
  387.  
  388. { To minimize disk access and speed things up, we read the file into a
  389.   scratchpad on the heap. Large files have to be done in two or more
  390.   chunks because of the 64K limit on dynamic memory variables. }
  391.  
  392. getmem(scratch, buffsize);                 { Allocate scratchpad }
  393. blockread(pcxfile, scratch^, 128);         { Get header into scratchpad }
  394.  
  395. { The .PCX file has a 128-byte header. Most of it can be ignored if you're
  396.   working with a known format. All we want is the palette information and
  397.   the length of the data line. }
  398.  
  399. move(scratch^, palbuf, 67);
  400. bytes_per_line:= palbuf[66];
  401.  
  402. {------------------------ Setup for CGA ---------------------------------}
  403.  
  404. if is_CGA then
  405. begin
  406.   getmem(buff0, 8000);      { Allocate memory for output }
  407.   getmem(buff1, 8000);
  408.   abuff0:= buff0;           { Make copies of pointers }
  409.   abuff1:= buff1;           
  410. end else
  411.  
  412. {----------------------- Setup for EGA/VGA ------------------------------}
  413.  
  414. begin
  415.   video_index:= 0;
  416.   port[$3C4]:= 2;           { Index to map mask register }
  417.   plane:= 1;                { Initialize plane }
  418.   port[$3C5]:= plane;       { Set sequencer to mask out other planes }
  419.  
  420. {-------------------- Decipher EGA/VGA palette --------------------------}
  421.  
  422. (* The palette information is stored in bytes 16-63 of the header. Each of
  423.    the 16 palette slots is allotted 3 bytes - one for each primary color.
  424.    Any of these bytes can have a value of 0-255.
  425.  
  426.    For the EGA there are just 4 significant settings, since only 64
  427.    different colors (4 x 4 x 4) are available. Hence for EGA-format images
  428.    we divide the codes by 64. The absolute color number for the palette
  429.    entry is derived by setting one of bits 0-2 and one of bits 3-5 with the
  430.    mask corresponding to the .PCX code byte. (In binary form, the absolute
  431.    color number may be thought of as 00RGBrgb.) This number is then passed
  432.    into Turbo's SetAllPalette procedure.
  433.  
  434.    For the VGA things work differently. Here we must use Turbo's
  435.    SetRGBPalette procedure to change the red, green, and blue values in the
  436.    16 active color registers. The registers expect values in the range 0-63
  437.    (64 x 64 x 64 = 256K, the number of possible colors), so we divide the
  438.    .PCX codes by 4. A further complication is that by default the palette
  439.    entries point to the color registers corresponding to the standard EGA
  440.    colors, so we must change them to point to registers 0-15 instead (or
  441.    else modify registers 0-5, 20, 7, and 56-63). See SHOWVGA.PAS for an
  442.    example of how to set the palette and the registers.
  443.  
  444.    Note that the palette works differently for the 200-line 16-color modes,
  445.    $0D and $0E. Because these modes use 4-bit palette entries, only the
  446.    default colors are available on the EGA, and their IDs don't correspond
  447.    to those in 350-line mode (e.g. 20 is bright red, not brown). Attempting
  448.    to set the palette with the data from the .PCX header will lead to odd
  449.    results in these modes, and in any case should not be necessary.
  450. *)
  451.  
  452.   for entry:= 0 to 15 do
  453.   begin
  454.     colorID:= 0;
  455.     for gun:= 0 to 2 do
  456.     begin
  457.       pcxcode:= palbuf[16 + entry * 3 + gun];   { Get primary color value }
  458.       if not is_VGA then
  459.       begin                                     { Interpret for EGA }
  460.         case (pcxcode div $40) of
  461.           0: mask:= $00;    { 000000 }
  462.           1: mask:= $20;    { 100000 }
  463.           2: mask:= $04;    { 000100 }
  464.           3: mask:= $24;    { 100100 }
  465.         end;
  466.         colorID:= colorID or (mask shr gun);    { Define two bits }
  467.       end  { not is_VGA }
  468.       else
  469.       begin  { is_VGA }
  470.         with RGBpal[entry] do                   { Interpret for VGA }
  471.         case gun of
  472.           0: redval:= pcxcode div 4;
  473.           1: greenval:= pcxcode div 4;
  474.           2: blueval:= pcxcode div 4;
  475.         end;
  476.       end;  { is_VGA }
  477.     end;  { gun }
  478.     if is_VGA then pal.colors[entry]:= entry
  479.               else pal.colors[entry]:= colorID;
  480.   end;  { entry }
  481.   pal.size:= 16;
  482. end;   { not is_CGA }
  483.  
  484. { ---------------- Read and decode the image data ----------------------- }
  485.  
  486. repeatcount:= 0;                        { Initialize assembler vars. }
  487. columncount:= 0;
  488. repeat
  489.   blockread(pcxfile, scratch^, buffsize, datalength);
  490.   if is_CGA then decode_CGA else decode_16;   { Call assembler routine }
  491. until eof(pcxfile);
  492. close(pcxfile);
  493. if not is_CGA then port[$3C5]:= $F;     { Reset mask map }
  494. freemem(scratch,buffsize);              { Discard scratchpad }
  495. end;  { READ_PCX_FILE }
  496.  
  497. { ========================= 256-color files ============================= }
  498.  
  499. procedure DECODE_PCX256; assembler;
  500.  
  501. (* Registers used:
  502.  
  503.    AL   data byte to be written to video
  504.    BX   end of input buffer
  505.    CL   number of times data byte is to be written
  506.    ES   output segment
  507.    DI   index into output buffer
  508.    DS   segment of input buffer
  509.    SI   index into input buffer
  510. *)
  511.  
  512. asm
  513. mov     es, page_addr       { video segment }
  514. mov     di, video_index     { index into video }
  515. xor     cx, cx              { clean up loop counter }
  516. mov     cl, repeatcount     { count in CL }
  517. mov     bx, datalength      { end of input buffer }
  518. push    ds                  { save DS }
  519. lds     si, scratch         { pointer to input in DS:SI }
  520. add     bx, si              { adjust datalength - SI may not be 0 }
  521. cld                         { clear DF }
  522. cmp     cl, 0               { was last byte a count? }
  523. jne     @multi_data         { yes, so next is data }
  524.  
  525. { --------------------- Loop through input buffer ----------------------- }
  526.  
  527. @getbyte:                   { last byte was not a count }
  528. cmp     si, bx              { end of input buffer? }
  529. je      @exit               { yes, quit }
  530. lodsb                       { get byte into AL, increment SI }
  531. cmp     al, 192             { test high bits }
  532. jb      @one_data           { not set, not a count }
  533. { It's a count byte }
  534. xor     al, 192             { get count from 6 low bits }
  535. mov     cl, al              { store repeat count }
  536. cmp     si, bx              { end of input buffer? }
  537. je      @exit               { yes, quit }
  538. @multi_data:
  539. lodsb                       { get byte into AL, increment SI }
  540. rep     stosb               { write byte CX times }
  541. jmp     @getbyte
  542. @one_data:
  543. stosb                       { byte into video }
  544. jmp     @getbyte
  545.  
  546. { ------------------------- Finished with buffer ------------------------ }
  547.  
  548. @exit:
  549. pop     ds                  { restore Turbo's data segment }
  550. mov     video_index, di     { save status for next run thru buffer }
  551. mov     repeatcount, cl
  552. end;  { asm }
  553.  
  554. { ================= Main procedure for 256-color files ================== }
  555.  
  556. procedure READ_PCX256(pfilename: pathstr);
  557.  
  558. var     x, gun, pcxcode: byte;
  559.         pcxfile: file;
  560.         palette_start, total_read: longint;
  561.         palette_flag: byte;
  562.         version: word;
  563.  
  564. procedure CLEANUP;
  565.  
  566. begin
  567. close(pcxfile);
  568. freemem(scratch, buffsize);
  569. end;
  570.  
  571. begin    { READ_PCX256 }
  572. assign(pcxfile, pfilename);
  573. {$I-} reset(pcxfile, 1);  {$I+}
  574. file_error:= (IOresult <> 0);
  575. if file_error then exit;
  576. getmem(scratch, buffsize);                  { Allocate scratchpad }
  577. blockread(pcxfile, version, 2);             { Read first two bytes }
  578. file_error:= (hi(version) < 5);             { No palette info. }
  579. if file_error then
  580. begin
  581.   cleanup; exit;
  582. end;
  583. palette_start:= filesize(pcxfile) - 769;
  584.  
  585. seek(pcxfile, 128);                        { Scrap file header }
  586. total_read:= 128;
  587.  
  588. repeatcount:= 0;                           { Initialize assembler vars. }
  589. video_index:= 0;
  590.  
  591. repeat
  592.   blockread(pcxfile, scratch^, buffsize, datalength);
  593.   inc(total_read, datalength);
  594.   if (total_read > palette_start) then
  595.       dec(datalength, total_read - palette_start);
  596.   decode_pcx256;
  597. until (eof(pcxfile)) or (total_read>= palette_start);
  598.  
  599. (* The last 769 btes of the file are palette information, starting with a
  600.    one-byte flag. Each group of three bytes represents the RGB values of
  601.    one of the color registers. The values have to be divided by 4 to be
  602.    brought within the range 0-63 expected by the registers. *)
  603.  
  604. seek(pcxfile, palette_start);
  605. blockread(pcxfile, palette_flag, 1);
  606. file_error:= (palette_flag <> 12);
  607. if file_error then
  608. begin
  609.   cleanup; exit;
  610. end;
  611. blockread(pcxfile, RGB256, 768);         { Get palette info. }
  612. for x:= 0 to 255 do
  613. with RGB256[x] do
  614. begin
  615.   redval:= redval shr 2;
  616.   greenval:= greenval shr 2;
  617.   blueval:= blueval shr 2;
  618. end;
  619. cleanup;
  620. end;  { READ_PCX256 }
  621.  
  622. { ========================== Initialization ============================= }
  623.  
  624. BEGIN
  625. page_addr:= page0;                      { Destination for EGA/VGA data }
  626. END.
  627.  
  628.